import time

from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver import ActionChains
from config import PATH
from tool import GetLog
from selenium.common.exceptions import StaleElementReferenceException


class BasePage(object):

    def __init__(self, driver, timeout=10):
        # 获取浏览器对象
        self.driver = driver  # 相当于：self.driver = DriverTools.get_driver()
        self.default_timeout = timeout  # 默认等待时间

    def fd_element_appear(self, loc):
        """
        元素定位 --> 元素出现（不一定可见）
        :param loc: 元素定位方式及属性值
        :return: 定位到的元素
        """
        try:
            element = WebDriverWait(self.driver, self.default_timeout, 1).until(ec.presence_of_element_located(loc))
            return element
        except Exception as e:
            GetLog.get_log().error(f"元素定位超时，定位信息：{loc}，错误详情：{e}")
            raise  # 重新抛出异常供上层处理

    def fd_element_visible(self, loc):
        """
        元素定位 --> 元素出现且可见
        :param loc: 元素定位方式及属性值
        :return: 定位到的元素
        """
        try:
            element = WebDriverWait(self.driver, self.default_timeout, 1).until(ec.visibility_of_element_located(loc))
            return element
        except Exception as e:
            GetLog.get_log().error(f"元素定位超时，定位信息：{loc}，错误详情：{e}")

    def fd_element_click(self, loc):
        """
        元素定位 --> 元素可点击
        :param loc: 元素定位方式及属性值
        :return: 定位到的元素
        """
        try:
            element = WebDriverWait(self.driver, self.default_timeout, 1).until(ec.element_to_be_clickable(loc))
            return element
        except Exception as e:
            GetLog.get_log().error(f"元素定位超时，定位信息：{loc}，错误详情：{e}")
            raise  # 重新抛出异常供上层处理

    def fd_wait_element_disappear(self, loc):
        """
        等待遮罩层消失（通用方法）
        :param loc: 遮罩层定位器，
        :return: None
        """
        try:
            WebDriverWait(self.driver, self.default_timeout).until(
                ec.visibility_of_element_located(loc)  # 等待元素可见,防止第一时间元素尚未加载就被识别为消失
            )
            WebDriverWait(self.driver, self.default_timeout).until(
                ec.invisibility_of_element_located(loc)  # 等待元素消失
            )
        except Exception as e:
            GetLog.get_log().error(f"元素定位超时，定位信息：{loc}，错误详情：{e}")
            raise



    def base_move_element_to_visible(self, loc):
        """
        元素移动到可见区域
        :param loc: 元素定位方式及属性值
        :return: 无
        """
        element = self.fd_element_appear(loc)
        self.driver.execute_script("arguments[0].scrollIntoView();", element)
        return element

    def base_input(self, loc, text):
        """
        输入框输入
        :param loc: 元素定位方式及属性值
        :param text: 输入内容
        :return: 无
        """
        # 定位元素
        element = self.fd_element_appear(loc)
        # 清空输入框
        element.clear()
        element.send_keys(text)

    def base_input_content_injection(self, loc, text):
        """
        元素内容注入 --> 常用于富文本框输入
        :param loc: 元素定位方式及属性值
        :param text: 输入内容
        :return: 无
        """
        element = self.fd_element_appear(loc)
        self.driver.execute_script(f"arguments[0].innerHTML = {text};", element)

    def base_select_list_by_test(self, loc, text):
        """
        下拉列表选择（根据文字）
        :param loc: 元素定位方式及属性值
        :param text: 选择的文本
        :return: 无
        """
        element = self.fd_element_appear(loc)
        Select(element).select_by_visible_text(text)

    def base_select_list_by_value(self, loc, text):
        """
        下拉列表选择（根据value值）
        :param loc: 元素定位方式及属性值
        :param text: 选择的文本
        :return: 无
        """
        element = self.fd_element_appear(loc)
        Select(element).select_by_value(text)

    def base_click(self, loc):
        """
        点击
        :param loc: 元素定位方式及属性值
        :return: 无
        """
        self.fd_element_click(loc).click()

    def base_visible_click(self, loc):
        """
        使用 JavaScript 滚动到可视区域再点击
        :param loc: 元素定位方式及属性值
        :return: 无
        """
        element = self.base_move_element_to_visible(loc)
        element.click()

    def base_js_click(self, loc):
        """
        使用 JavaScript 强制点击
        :param loc: 元素定位方式及属性值
        :return: 无
        """
        element = self.fd_element_appear(loc)
        self.driver.execute_script("arguments[0].click();", element)

    def base_double_click(self, loc):
        """
        双击
        :param loc: 元素定位方式及属性值
        :return: 无
        """
        action = ActionChains(self.driver)
        action.double_click(self.fd_element_click(loc)).perform()

    def base_right_click(self, loc):
        """
        右击
        :param loc: 元素定位方式及属性值
        :return: 无
        """
        action = ActionChains(self.driver)
        action.context_click(self.fd_element_click(loc)).perform()

    def base_hover(self, loc):
        """
        鼠标悬停
        :param loc: 元素定位方式及属性值
        :return: 无
        """
        action = ActionChains(self.driver)
        action.move_to_element(self.fd_element_visible(loc)).perform()

    def base_click_retry(self, loc, max_retry=3):
        """
        元素定位 --> 尝试多次定位（用于页面数据重新加载前后有相同的数据，识别时因加载速度引起的元素过期问题）
        :param loc: 元素定位方式及属性值
        :param max_retry: 最大尝试次数
        :return: 定位到的元素
        """
        retry = 0
        while retry < max_retry:
            try:
                ele = self.fd_element_click(loc)
                return ele.click()
            except StaleElementReferenceException:
                retry += 1
                time.sleep(0.5)
                if retry == max_retry:
                    raise

    def base_get_text(self, loc):
        """
        获取元素文本
        :param loc: 元素定位方式及属性值
        :return: 元素文本
        """
        text = self.fd_element_visible(loc).text
        return text

    def base_get_title(self):
        """
        获取页面标题
        :return: 页面标题
        """
        return self.driver.title

    def base_get_current_url(self):
        """
        获取当前页面的URL
        :return: 当前页面的URL
        """
        return self.driver.current_url

    def base_switch_handle(self, loc):
        """
        切换多窗口并获取指定元素
        :param loc: 定位的元素信息
        :return: 第二个窗口的页面元素
        """
        # 等待页面加载
        WebDriverWait(self.driver, self.default_timeout).until(lambda x: len(x.window_handles) > 1)
        handles = self.driver.window_handles
        self.driver.switch_to.window(handles[1])
        # 切换到新窗口进行定位
        element = self.fd_element_appear(loc)
        return element

    def base_switch_frame(self, loc):
        """
        切换frame
        :param loc: frame的定位信息
        :return: 无
        """
        frame_ele = self.fd_element_appear(loc)
        self.driver.switch_to.frame(frame_ele)

    def base_default_frame(self):
        """
        切换到默认frame
        :return: 无
        """
        self.driver.switch_to.default_content()

    def get_shot(self, file_name):
        """
        截图
        :param file_name: 截图文件名
        :return:无
        """
        self.driver.get_screenshot_as_file(PATH + '/img/' + file_name)
